package net.oxygen.upload.service.impl;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.util.IdUtil;
import io.minio.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.oxygen.upload.enums.BizCodeEnum;
import net.oxygen.upload.exception.BizException;
import net.oxygen.upload.model.MultipartUploadCreate;
import net.oxygen.upload.model.UploadPartCreate;
import net.oxygen.upload.service.IMinioService;
import net.oxygen.upload.util.CustomMinioClient;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;

/**
 * <p>
 * 功能：Minio服务实现类
 * </p>
 *
 * @author 陈伟伟
 * @since 2023/12/18 11:26
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class MinioServiceImpl implements IMinioService {

    private final MinioClient minioClient;

    private final CustomMinioClient customMinioClient;

    /**
     * 上传文件到指定的存储桶
     * 普通上传，适用于小文件
     *
     * @param file  需要上传的文件
     * @param bucketName  存储桶名称
     * @return 上传结果字符串
     */
    @Override
    public String uploadFile(MultipartFile file, String bucketName) {
        try (InputStream in = file.getInputStream()) {
            String extName = FileNameUtil.extName(file.getOriginalFilename());
            String fileName = IdUtil.fastSimpleUUID() + "." + extName;
            String path = DateUtil.format(new DateTime(), "yyyy/MM/dd");
            String objectName = path + "/" + fileName;
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .stream(in, file.getSize(), -1)
                            .contentType(file.getContentType())
                            .build()
            );
            return objectName;
        } catch (Exception e) {
            log.error("文件上传失败", e);
            throw new BizException(BizCodeEnum.FILE_UPLOAD_FAIL);
        }
    }

    /**
     * 跟据文件信息返回唯一的上传ID
     *
     * @param uploadCreate 文件上传的信息
     * @return 包含上传ID的响应
     * @throws BizException 如果在上传过程中发生错误
     */
    @Override
    public CreateMultipartUploadResponse uploadId(MultipartUploadCreate uploadCreate) {
        try {
            return customMinioClient.createMultipartUploadAsync(
                    uploadCreate.getBucketName(),
                    uploadCreate.getRegion(),
                    uploadCreate.getObjectName(),
                    uploadCreate.getHeaders(),
                    uploadCreate.getExtraQueryParams()
            ).get();
        } catch (Exception e) {
            log.error("获取文件上传编号失败", e);
            throw new BizException(BizCodeEnum.FILE_UPLOAD_FAIL);
        }
    }

    /**
     * 上传文件分片
     * @param uploadPartCreate 上传分片的信息
     * @return 上传结果
     */
    @Override
    public UploadPartResponse uploadPart(UploadPartCreate uploadPartCreate) {
        try {
            return customMinioClient.uploadPartAsync(
                    uploadPartCreate.getBucketName(),
                    uploadPartCreate.getRegion(),
                    uploadPartCreate.getObjectName(),
                    uploadPartCreate.getData(),
                    uploadPartCreate.getLength(),
                    uploadPartCreate.getUploadId(),
                    uploadPartCreate.getPartNumber(),
                    uploadPartCreate.getHeaders(),
                    uploadPartCreate.getExtraQueryParams()
            ).get();
        } catch (Exception e) {
            log.error("上传分片失败", e);
            throw new BizException(BizCodeEnum.FILE_UPLOAD_FAIL);
        }
    }

    /**
     * 获取已上传的文件分片信息
     * @param uploadCreate 上传文件的信息
     * @return 已上传的分片
     */
    @Override
    public ListPartsResponse listMultipartUploads(MultipartUploadCreate uploadCreate) {
        try {
            return customMinioClient.listPartsAsync(
                    uploadCreate.getBucketName(),
                    uploadCreate.getRegion(),
                    uploadCreate.getObjectName(),
                    uploadCreate.getMaxParts(),
                    uploadCreate.getPartNumberMarker(),
                    uploadCreate.getUploadId(),
                    uploadCreate.getHeaders(),
                    uploadCreate.getExtraQueryParams()
            ).get();
        } catch (Exception e) {
            log.error("获取分片列表失败", e);
            throw new BizException(BizCodeEnum.FILE_UPLOAD_FAIL);
        }
    }

    /**
     * 合并分片
     * @param uploadCreate 上传文件的信息
     * @return 合并结果
     */
    @Override
    public ObjectWriteResponse completeMultipartUpload(MultipartUploadCreate uploadCreate) {
        try {
            return customMinioClient.completeMultipartUploadAsync(
                    uploadCreate.getBucketName(),
                    uploadCreate.getRegion(),
                    uploadCreate.getObjectName(),
                    uploadCreate.getUploadId(),
                    uploadCreate.getParts(),
                    uploadCreate.getHeaders(),
                    uploadCreate.getExtraQueryParams()
            ).get();
        } catch (Exception e) {
            log.error("合并分片失败", e);
            throw new BizException(BizCodeEnum.FILE_UPLOAD_FAIL);
        }
    }

}
